#pragma once

#include <iostream>
#include <queue>
#include <vector>
#include <future>
#include "memory"

class ThreadPool {
public:
    ThreadPool(): m_thread_size(1), m_terminate(false) {};
    ~ThreadPool() {
        stop();
    }
    // 线程池初始化
    bool init(size_t size);
    // 停止所有线程
    void stop();
    // 任务执行入口
    template <class F, class... A>
    auto exec(F&& f, A&&... args)->std::future<decltype(f(args...))>;
    // 等待所有线程执行完成
    bool waitDone();

private:
    // 每个任务都是一个struct结构体，方便未来扩展
    struct Task {
        Task() {}
        std::function<void()> m_func;
    };
    // Task未来在队列中以智能指针的形式传递
    typedef std::shared_ptr<Task> TaskFunc;

private:
    // 尝试从任务队列获取一个任务
    bool get(TaskFunc &t);
    // 线程主函数
    bool run();

private:
    // 任务队列，将Task直接放到队列中
    std::queue<TaskFunc> m_tasks;
    // 线程池
    std::vector<std::thread*> m_threads;
    // 锁
    std::mutex m_mutex;
    // 用于线程之间通知的条件变量
    std::condition_variable m_cond;
    // 线程池大小
    size_t m_thread_size;
    // 标记线程是否结束
    bool m_terminate;
    // 用来记录状态的原子变量
    std::atomic<int> m_atomic{0};
};


bool ThreadPool::init(size_t size) {
    std::unique_lock<std::mutex> lock(m_mutex);
    if (!m_threads.empty()) {
        return false;
    }
    m_thread_size = size;
    for (int i = 0; i < m_thread_size; ++i) {
        m_threads.push_back(new std::thread(&ThreadPool::run, this));
    }
    return true;
}

bool ThreadPool::run() {
    while (!m_terminate) {
        TaskFunc task;
        bool ok = get(task);
        if (ok) {
            ++m_atomic;
            task->m_func();
            --m_atomic;

            std::unique_lock<std::mutex> lock(m_mutex);
            if (m_atomic == 0 && m_tasks.empty()) {
                m_cond.notify_all();
            }
        }
    }
}

bool ThreadPool::get(TaskFunc &t) {
    std::unique_lock<std::mutex> lock(m_mutex);
    if (m_tasks.empty()) {
        m_cond.wait(lock, [this]()->bool { return m_terminate || !m_tasks.empty(); });
    }
    if (m_terminate) {
        return false;
    }
    t = std::move(m_tasks.front());
    m_tasks.pop();
    return true;
}

// 这行代码是函数模板的声明。它接受两个模板参数：F 表示可调用对象的类型，A... 表示参数包（parameter pack），
// 表示可变数量的参数。函数返回类型使用了尾置返回类型语法（trailing return type），根据参数和可调用对象的返回类型推导出函数的返回类型。
template<class F, class ...A>
auto ThreadPool::exec(F &&f, A &&...args) -> std::future<decltype(f(args...))> {
    using retType = decltype(f(args...));
    // 这行代码创建了一个 std::packaged_task 对象，用于包装可调用对象。std::packaged_task 是一个可以异步执行的任务。
    // bind 函数用于绑定可调用对象 f 和参数包 args 到 std::packaged_task 对象中，std::forward 用于保持参数的完美转发性质。
    auto task = std::make_shared<std::packaged_task<retType()>>(std::bind(std::forward<F>(f), std::forward<A>(args)...));
    TaskFunc fPtr = std::make_shared<Task>();
    // 这行代码使用 lambda 表达式将任务绑定到 fPtr->m_func 中，当 fPtr->m_func 被调用时，它将执行 task 中包装的可调用对象。
    fPtr->m_func = [task](){
        (*task)();
    };

    std::unique_lock<std::mutex> lock(m_mutex);
    m_tasks.push(fPtr);
    m_cond.notify_one();
    // 函数返回一个 std::future 对象，用于异步获取可调用对象的返回值。
    // task->get_future() 返回与 std::packaged_task 关联的 std::future 对象，该对象会在任务执行完成后接收返回值。
    return task->get_future();
}




void ThreadPool::stop() {
    {
        std::unique_lock<std::mutex> lock(m_mutex);
        m_terminate = true;
        m_cond.notify_all();
    }

    for (auto &e : m_threads) {
        if (e->joinable()) {
            e->join();
        }
        delete e;
        e = nullptr;
    }
    std::unique_lock<std::mutex> lock(m_mutex);
    m_threads.clear();
}